#include "EMT.h"
#include <chrono>
#include <omp.h>
#include <windows.h>
#include <cmath>
#include <thread>
#define PI 3.14159265358979323846 // pi

void EMT::bar_print(bool *is_simulation_end, double *end_time, double *simulation_time)
{
    double t_print = *end_time / 10000.0;
    int count = 0;
    printf("\n");
    while (!*is_simulation_end)
    {
        if (*simulation_time >= t_print * count)
        {
            count++;
            printf("\rRunning[%.2lf%%]:%.6lf/%.6lf.", *simulation_time / *end_time * 100, *simulation_time, *end_time);
        }
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

void EMT::file_print()
{
    while (1)
    {
        std::unique_lock<std::mutex> ul(mtx);
        cv.wait(ul,
                [this]
                {
                    if (!omgr.title_printed)
                    {
                        omgr.title_printed = true;
                        for (auto &&title : omgr.titles)
                            omgr.fout << title << ",";
                        omgr.fout << endl;
                    }
                    vector<double> sdata = omgr.data.front();
                    for (auto &&i : sdata)
                        omgr.fout << i << ",";
                    omgr.fout << endl;
                    omgr.data.pop();
                    return 0;
                });
    }
}

void EMT::solve()
{
    auto start_time = std::chrono::system_clock::now();

    bool is_simulation_end = false;

    begin();

    thread print_bar(EMT::bar_print, &is_simulation_end, &end_time, &simulation_time);
    print_bar.detach();

    thread print_file(&EMT::file_print, this);
    print_file.detach();
    // 类成员函数自带参数this指针，作为泛函传入时，参数不匹配，需要指定为静态的，或者将类的this指针传入

    for (simulation_time = 0; simulation_time < end_time; simulation_time += delt)
    {
        if (!timezero())
        {
            dsout_out_iteration();
        }

        control();
        dsdyn_out_iteration();

        double max_err;
        ec.V_last_iteration.setZero();
        iteration_count = 0;
        while (if_iteration)
        {
            if (!timezero())
            {
                dsout_in_iteration();
            }
            dsdyn_in_iteration();
            emtdc();

            if (timezero())
            {
                breaktype = 0;
                break;
            }
            if (iteration_count++ >= iteration_max)
            {
                // printf("iteration max exceeded at %.12lf s.\n", simulation_time);
                iteration_count = 0;
                breaktype = 1;
                break;
            }
            for (int i = 0; i < num_node - 1; i++)
            {
                err.push_back(fabs(V(i) - V_last_iteration(i)));
            }
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 8; j++)
                    err.push_back(fabs(IGBT[i].V_IGBT[j] - IGBT[i].V_IGBT_last[j]));

            max_err = my_max(err);
            if (max_err <= eps)
            {
                breaktype = 2;
                break;
            }
            V_last_iteration = V;
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 8; j++)
                    IGBT[i].V_IGBT_last[j] = IGBT[i].V_IGBT[j];
            err.clear();
        }

        std::unique_lock<std::mutex> ul(mtx); // 访问线程共享变量时加锁
        if (omgr.interval_count++ >= omgr.interval_threshold)
        {
            omgr.interval_count = 0;
            omgr.add(simulation_time, "t");
            omgr.add(C.VC, "VC");
            omgr.add(C.IC, "IC");
            omgr.add((V(1) - V(2)) * Gload, "Iload");
            omgr.add(L.VL, "VL");
            omgr.add(L.IL, "IL");
            for (int i = 0; i < 4; i++)
            {
                omgr.add(IGBT[i].Vex(0) * 1e-3, "V" + to_string(i));
                omgr.add(IGBT[i].Itotal * 1e-3, "I" + to_string(i));
                omgr.add(IGBT[i].Gout, "Gout" + to_string(i));
                omgr.add(IGBT[i].Jout * 1.0e-3, "Jout" + to_string(i));
            }
            omgr.add(vsource.Vsource, "vsource");

            for (auto i = 0; i < 4; i++)
                omgr.add(IGBT[i].sig, "sig" + to_string(i));
            omgr.add(H_sig, "H_sig");
            omgr.add(V(0), "V_valve");

            omgr.add(breaktype, "breaktype");
            omgr.add(iteration_count, "iteration_count");
            omgr.integrate();
        }
        if (omgr.data.size() > 1000)
        {
            cv.notify_one();
        }
    }
    cv.notify_one();
    is_simulation_end = true;
    auto end_simulation_time = std::chrono::system_clock::now();
    double duration_microsecond = std::chrono::duration<double, std::micro>(end_simulation_time - start_time).count();
    std::cout << "\n\n[1]Simulation completed successfully in " << duration_microsecond / 1000 << " ms." << endl;
}

void EMT::begin()
{
    pool = new ThreadPool(6);

    model_path = "./model/DAB.model";
    param Params = load(model_path);

    end_time = Params("end_time");
    delt = Params("delt");

    Params("delt") = 1.0e-6;
    Params("vsource_Vamp") = 2.5;
    Params("vsource_type") = 1; // 1代表DC
    Params("vsource_r") = 1.0;
    Params("Rload") = 1.0;
    Params("rGND1") = 1.0e12;
    Params("rGND2") = 1.0e12;
    Params("C0") = 500;
    Params("C1") = 3500;
    Params("Le") = 0.424;
    Params("L1") = 0.088;
    Params("L2") = 0.088;
    Params("Im") = 0.01;
    Params("Vtr_1") = 2.0;
    Params("Vtr_2") = 1;
    Params("Smva") = 2;
    Params("freq") = 15000;

    ec.begin(Params);
}

void EMT::control()
{
    // double modulation_wave = vsource.Vamp * sqrt(2) * sin(2 * vsource.f * PI * (simulation_time - 15 / 360 / vsource.f));
    // pwm.set(10000, vsource.Vamp * sqrt(2) * 1.1, &simulation_time);
    // H_sig = pwm.compare(modulation_wave);

    // switch (H_sig)
    // {
    // case 0:
    //     IGBT[0].setSig(1);
    //     IGBT[1].setSig(0);
    //     IGBT[2].setSig(1);
    //     IGBT[3].setSig(0);
    //     break;
    // case 1:
    //     IGBT[0].setSig(1);
    //     IGBT[1].setSig(0);
    //     IGBT[2].setSig(0);
    //     IGBT[3].setSig(1);
    //     break;
    // case -1:
    //     IGBT[0].setSig(0);
    //     IGBT[1].setSig(1);
    //     IGBT[2].setSig(1);
    //     IGBT[3].setSig(0);
    //     break;
    // }
    ec.control(simulation_time);
}

void EMT::dsdyn_in_iteration()
{

    for (int i = 0; i < 4; i++)
    {
        IGBT[i].dsdyn_in_iteration(delt, simulation_time);
    }

    Y.setZero();
    J.setZero();
    Yg.setZero();

    Yg(0, 0) = vsource.g;
    Yg(1, 1) = IGBT[0].Gout;
    Yg(2, 2) = IGBT[1].Gout;
    Yg(3, 3) = IGBT[2].Gout;
    Yg(4, 4) = IGBT[3].Gout;
    Yg(5, 5) = C.GC;
    Yg(6, 6) = Gload;
    Yg(7, 7) = L.GL;

    Y = A * Yg * A.transpose();

    if (timezero())
        fout << Y << endl;

    J(0) = -IGBT[0].Jout + IGBT[1].Jout + L.Ihis;
    J(1) = IGBT[0].Jout + IGBT[2].Jout - C.Ihis;
    J(2) = -IGBT[1].Jout - IGBT[3].Jout + C.Ihis;
    J(3) = -L.Ihis + vsource.Isource;
}

void EMT::dsdyn_out_iteration()
{
    ec.dsdyn_out_iteration(delt, simulation_time);
}

void EMT::emtdc()
{
    V = Y.inverse() * J;
}

void EMT::dsout_in_iteration()
{
    ec.dsout_in_iteration();
    IGBT[0].Vex(0) = V(1) - V(0);
    IGBT[1].Vex(0) = V(0) - V(2);
    IGBT[2].Vex(0) = V(1);
    IGBT[3].Vex(0) = -V(2);

    for (int i = 0; i < 4; i++)
    {
        IGBT[i].Vex(0) *= 1.0e3; // IGBT中单位为V和A，外部单位为kV和kA
        IGBT[i].dsout_in_iteration();
    }
}

void EMT::dsout_out_iteration()
{
    for (int i = 0; i < 4; i++)
        IGBT[i].dsout_out_iteration();

    C.VC = V(1) - V(2);
    C.dsout();

    L.VL = V(3) - V(0);
    L.dsout();
}

double EMT::my_max(vector<double> &data)
{
    int max_index = 0;
    for (int i = 1; i < data.size(); i++)
    {
        if (data[max_index] < data[i])
            max_index = i;
    }
    return data[max_index];
}

param EMT::load(string path)
{
    ifstream fin(path);
    if (!fin.is_open())
    {
        printf("Failed to open model [%s].\n", path.c_str());
        exit(-1);
    }
    string line;
    enum class Section
    {
        None,
        Parameter,
        Vmap,
        Branch,
        EndPage
    };
    Section section = Section::None;
    param Params;
    while (getline(fin, line))
    {
        if (line.find("Parameter:") != string::npos)
        {
            section = Section::Parameter;
            continue;
        }
        else if (line.find("Branch:") != string::npos)
        {
            section = Section::Branch;
            continue;
        }
        else if (line.find("Vmap:") != string::npos)
        {
            section = Section::Vmap;
            continue;
        }
        else if (line.find("EndPage:") != string::npos)
        {
            section = Section::EndPage;
            break;
        }
        stringstream ss(line);
        if (section == Section::Parameter)
        {
            string key;
            ss >> key;
            ss >> Params(key);
            continue;
        }
        else if (section == Section::Vmap)
        {
            string key;
            ss >> key;
            ss >> ec.brs.Vmap[key];
            continue;
        }
        else if (section == Section::Branch)
        {
            string br_name, from_tag, end_tag;
            ss >> br_name >> from_tag >> end_tag;
            ec.brs.add_br(from_tag, end_tag, br_name);
            continue;
        }
        else if (section == Section::EndPage)
        {
            break;
        }
    }
    return Params;
}